//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package project_code_api_serv

import (
	"context"
	"time"

	"atomgit.com/openlinksaas/api-server/dao"
	"atomgit.com/openlinksaas/api-server/dao/code_dao"
	"atomgit.com/openlinksaas/api-server/dao/user_dao"
	"atomgit.com/openlinksaas/api-server/utils"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_code_api"
	"github.com/google/uuid"
	"go.mongodb.org/mongo-driver/mongo"
)

type ProjectCodeApiImpl struct {
	project_code_api.UnimplementedProjectCodeApiServer
}

func (apiImpl *ProjectCodeApiImpl) ListThread(ctx context.Context, req *project_code_api.ListThreadRequest) (*project_code_api.ListThreadResponse, error) {
	emptyList := []*project_code_api.ThreadInfo{}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_code_api.ListThreadResponse{
			Code:       project_code_api.ListThreadResponse_CODE_WRONG_SESSION,
			ErrMsg:     "会话不存在",
			ThreadList: emptyList,
		}, nil
	}
	//检查权限
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_code_api.ListThreadResponse{
			Code:       project_code_api.ListThreadResponse_CODE_NO_PROJECT,
			ErrMsg:     "项目不存在",
			ThreadList: emptyList,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_code_api.ListThreadResponse{
			Code:       project_code_api.ListThreadResponse_CODE_NO_PERMISSION,
			ErrMsg:     "没有权限",
			ThreadList: emptyList,
		}, nil
	} else if err != nil {
		return nil, err
	}

	count, err := code_dao.ThreadDao.Count(ctx, req.ProjectId)
	if err != nil {
		return nil, err
	}
	threadItemList, err := code_dao.ThreadDao.List(ctx, req.ProjectId, int64(req.Offset), int64(req.Limit))
	if err != nil {
		return nil, err
	}
	//补充用户信息
	userIdList := []string{}
	commitIdList := []string{}
	for _, threadItem := range threadItemList {
		userIdList = append(userIdList, threadItem.CommentUserIdList...)
		commitIdList = append(commitIdList, threadItem.LastCommentId)
	}
	userItemList, err := user_dao.UserInfoDao.ListById(ctx, userIdList)
	if err != nil {
		return nil, err
	}
	userItemMap := map[string]*user_dao.UserInfoDbItem{}
	for _, userItem := range userItemList {
		userItemMap[userItem.UserId] = userItem
	}
	//获取最后评论
	commitItemList, err := code_dao.CommentDao.ListById(ctx, commitIdList)
	if err != nil {
		return nil, err
	}
	commitItemMap := map[string]*code_dao.CommentDbItem{}
	for _, commitItem := range commitItemList {
		commitItemMap[commitItem.CommentId] = commitItem
	}
	//生成结果
	threadList := []*project_code_api.ThreadInfo{}
	for _, threadItem := range threadItemList {
		commitItem, ok := commitItemMap[threadItem.LastCommentId]
		if !ok {
			continue
		}
		threadInfo := threadItem.ToThreadInfo(commitItem.ToComment())
		for _, commentUser := range threadInfo.CommentUserList {
			userItem, ok := userItemMap[commentUser.CommentUserId]
			if ok {
				commentUser.CommentDisplayName = userItem.BasicInfo.DisplayName
				commentUser.CommentLogoUri = userItem.BasicInfo.LogoUri
				if userItem.UserId == threadInfo.LastComment.UserId {
					threadInfo.LastComment.UserDisplayName = userItem.BasicInfo.DisplayName
					threadInfo.LastComment.UserLogoUri = userItem.BasicInfo.LogoUri
				}
			}
		}
		threadList = append(threadList, threadInfo)
	}
	return &project_code_api.ListThreadResponse{
		Code:       project_code_api.ListThreadResponse_CODE_OK,
		TotalCount: count,
		ThreadList: threadList,
	}, nil
}

func (apiImpl *ProjectCodeApiImpl) AddComment(ctx context.Context, req *project_code_api.AddCommentRequest) (*project_code_api.AddCommentResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_code_api.AddCommentResponse{
			Code:   project_code_api.AddCommentResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	//检查权限
	projectItem, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_code_api.AddCommentResponse{
			Code:   project_code_api.AddCommentResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_code_api.AddCommentResponse{
			Code:   project_code_api.AddCommentResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	if projectItem.Closed {
		return &project_code_api.AddCommentResponse{
			Code:   project_code_api.AddCommentResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	commentId := uuid.NewString()
	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = code_dao.CommentDao.Insert(sessCtx, &code_dao.CommentDbItem{
			CommentId:   commentId,
			ThreadId:    req.ThreadId,
			ProjectId:   req.ProjectId,
			ContentType: uint32(req.ContentType),
			Content:     req.Content,
			UserId:      sessionItem.UserId,
			CreateTime:  nowTime,
			UpdateTime:  nowTime,
		})
		if err != nil {
			return nil, err
		}
		count, err := code_dao.CommentDao.CountByThread(sessCtx, req.ProjectId, req.ThreadId)
		if err != nil {
			return nil, err
		}
		userIdList, err := code_dao.CommentDao.DistinctUserId(sessCtx, req.ProjectId, req.ThreadId)
		if err != nil {
			return nil, err
		}
		exist, err := code_dao.ThreadDao.Exist(sessCtx, req.ProjectId, req.ThreadId)
		if err != nil {
			return nil, err
		}
		if exist {
			err = code_dao.ThreadDao.Update(sessCtx, req.ProjectId, req.ThreadId, nowTime, count, userIdList, commentId)
			if err != nil {
				return nil, err
			}
		} else {
			err = code_dao.ThreadDao.Insert(ctx, &code_dao.ThreadInfoDbItem{
				ThreadId:          req.ThreadId,
				ProjectId:         req.ProjectId,
				CreateTime:        nowTime,
				UpdateTime:        nowTime,
				CommentCount:      count,
				CommentUserIdList: userIdList,
				LastCommentId:     commentId,
			})
			if err != nil {
				return nil, err
			}
		}
		return &project_code_api.AddCommentResponse{
			Code:      project_code_api.AddCommentResponse_CODE_OK,
			CommentId: commentId,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addAddCommentEvent(sessionItem, req, projectItem, commentId, nowTime)
	return ret.(*project_code_api.AddCommentResponse), nil
}

func (apiImpl *ProjectCodeApiImpl) UpdateComment(ctx context.Context, req *project_code_api.UpdateCommentRequest) (*project_code_api.UpdateCommentResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_code_api.UpdateCommentResponse{
			Code:   project_code_api.UpdateCommentResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	//检查权限
	projectItem, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_code_api.UpdateCommentResponse{
			Code:   project_code_api.UpdateCommentResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_code_api.UpdateCommentResponse{
			Code:   project_code_api.UpdateCommentResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	if projectItem.Closed {
		return &project_code_api.UpdateCommentResponse{
			Code:   project_code_api.UpdateCommentResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}
	commentItem, err := code_dao.CommentDao.Get(ctx, req.ProjectId, req.CommentId)
	if err != nil {
		return &project_code_api.UpdateCommentResponse{
			Code:   project_code_api.UpdateCommentResponse_CODE_NO_COMMENT,
			ErrMsg: "代码评论不存在",
		}, nil
	}
	if commentItem.UserId != sessionItem.UserId {
		return &project_code_api.UpdateCommentResponse{
			Code:   project_code_api.UpdateCommentResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = code_dao.CommentDao.UpdateContent(sessCtx, req.ProjectId, req.CommentId, uint32(req.ContentType), req.Content, nowTime)
		if err != nil {
			return nil, err
		}
		return &project_code_api.UpdateCommentResponse{
			Code: project_code_api.UpdateCommentResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addUpdateCommentEvent(sessionItem, req, projectItem, commentItem, nowTime)
	return ret.(*project_code_api.UpdateCommentResponse), nil
}

func (apiImpl *ProjectCodeApiImpl) ListComment(ctx context.Context, req *project_code_api.ListCommentRequest) (*project_code_api.ListCommentResponse, error) {
	emptyList := []*project_code_api.Comment{}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_code_api.ListCommentResponse{
			Code:        project_code_api.ListCommentResponse_CODE_WRONG_SESSION,
			ErrMsg:      "会话不存在",
			CommentList: emptyList,
		}, nil
	}
	//检查权限
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_code_api.ListCommentResponse{
			Code:        project_code_api.ListCommentResponse_CODE_NO_PROJECT,
			ErrMsg:      "项目不存在",
			CommentList: emptyList,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_code_api.ListCommentResponse{
			Code:        project_code_api.ListCommentResponse_CODE_NO_PERMISSION,
			ErrMsg:      "没有权限",
			CommentList: emptyList,
		}, nil
	} else if err != nil {
		return nil, err
	}

	commentItemList, err := code_dao.CommentDao.ListByThread(ctx, req.ProjectId, req.ThreadId)
	if err != nil {
		return nil, err
	}
	//补充成员信息
	userIdList := []string{}
	for _, commentItem := range commentItemList {
		userIdList = append(userIdList, commentItem.UserId)
	}
	userItemList, err := user_dao.UserInfoDao.ListById(ctx, userIdList)
	if err != nil {
		return nil, err
	}
	userItemMap := map[string]*user_dao.UserInfoDbItem{}
	for _, userItem := range userItemList {
		userItemMap[userItem.UserId] = userItem
	}
	commentList := []*project_code_api.Comment{}
	//生成结果
	for _, commentItem := range commentItemList {
		comment := commentItem.ToComment()
		userItem, ok := userItemMap[comment.UserId]
		if ok {
			comment.UserDisplayName = userItem.BasicInfo.DisplayName
			comment.UserLogoUri = userItem.BasicInfo.LogoUri
		}
		if commentItem.UserId == sessionItem.UserId {
			comment.CanUpdate = true
		}
		if commentItem.UserId == sessionItem.UserId || projectItem.OwnerUserId == sessionItem.UserId || roleItem.BasicInfo.Admin {
			comment.CanRemove = true
		}
		commentList = append(commentList, comment)
	}
	return &project_code_api.ListCommentResponse{
		Code:        project_code_api.ListCommentResponse_CODE_OK,
		CommentList: commentList,
	}, nil
}

func (apiImpl *ProjectCodeApiImpl) GetComment(ctx context.Context, req *project_code_api.GetCommentRequest) (*project_code_api.GetCommentResponse, error) {
	emptyComment := &project_code_api.Comment{}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_code_api.GetCommentResponse{
			Code:    project_code_api.GetCommentResponse_CODE_WRONG_SESSION,
			ErrMsg:  "会话不存在",
			Comment: emptyComment,
		}, nil
	}
	//检查权限
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_code_api.GetCommentResponse{
			Code:    project_code_api.GetCommentResponse_CODE_NO_PROJECT,
			ErrMsg:  "项目不存在",
			Comment: emptyComment,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_code_api.GetCommentResponse{
			Code:    project_code_api.GetCommentResponse_CODE_NO_PERMISSION,
			ErrMsg:  "没有权限",
			Comment: emptyComment,
		}, nil
	} else if err != nil {
		return nil, err
	}

	commentItem, err := code_dao.CommentDao.Get(ctx, req.ProjectId, req.CommentId)
	if err != nil {
		return &project_code_api.GetCommentResponse{
			Code:    project_code_api.GetCommentResponse_CODE_NO_COMMENT,
			ErrMsg:  "代码评论不存在",
			Comment: emptyComment,
		}, nil
	}
	userItemList, err := user_dao.UserInfoDao.ListById(ctx, []string{commentItem.UserId})
	if err != nil {
		return nil, err
	}
	comment := commentItem.ToComment()
	if len(userItemList) == 1 {
		comment.UserDisplayName = userItemList[0].BasicInfo.DisplayName
		comment.UserLogoUri = userItemList[0].BasicInfo.LogoUri
	}
	if commentItem.UserId == sessionItem.UserId {
		comment.CanUpdate = true
	}
	if commentItem.UserId == sessionItem.UserId || projectItem.OwnerUserId == sessionItem.UserId || roleItem.BasicInfo.Admin {
		comment.CanRemove = true
	}
	return &project_code_api.GetCommentResponse{
		Code:    project_code_api.GetCommentResponse_CODE_OK,
		Comment: comment,
	}, nil
}

func (apiImpl *ProjectCodeApiImpl) RemoveComment(ctx context.Context, req *project_code_api.RemoveCommentRequest) (*project_code_api.RemoveCommentResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_code_api.RemoveCommentResponse{
			Code:   project_code_api.RemoveCommentResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	//检查权限
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_code_api.RemoveCommentResponse{
			Code:   project_code_api.RemoveCommentResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_code_api.RemoveCommentResponse{
			Code:   project_code_api.RemoveCommentResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	if projectItem.Closed {
		return &project_code_api.RemoveCommentResponse{
			Code:   project_code_api.RemoveCommentResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	commentItem, err := code_dao.CommentDao.Get(ctx, req.ProjectId, req.CommentId)
	if err != nil {
		return &project_code_api.RemoveCommentResponse{
			Code:   project_code_api.RemoveCommentResponse_CODE_NO_COMMENT,
			ErrMsg: "代码评论不存在",
		}, nil
	}
	//管理员和用户可以进行删除
	if !(sessionItem.UserId == projectItem.OwnerUserId || sessionItem.UserId == commentItem.UserId || roleItem.BasicInfo.Admin) {
		return &project_code_api.RemoveCommentResponse{
			Code:   project_code_api.RemoveCommentResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}
	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = code_dao.CommentDao.Remove(sessCtx, req.ProjectId, req.CommentId)
		if err != nil {
			return nil, err
		}
		count, err := code_dao.CommentDao.CountByThread(sessCtx, req.ProjectId, commentItem.ThreadId)
		if err != nil {
			return nil, err
		}
		if count == 0 {
			err = code_dao.ThreadDao.Remove(sessCtx, req.ProjectId, commentItem.ThreadId)
			if err != nil {
				return nil, err
			}
		} else {
			userIdList, err := code_dao.CommentDao.DistinctUserId(sessCtx, req.ProjectId, commentItem.CommentId)
			if err != nil {
				return nil, err
			}
			lastCommentItem, err := code_dao.CommentDao.GetLastComment(sessCtx, req.ProjectId, commentItem.ThreadId)
			if err != nil {
				return nil, err
			}
			err = code_dao.ThreadDao.Update(sessCtx, req.ProjectId, commentItem.CommentId, lastCommentItem.CreateTime, count, userIdList, lastCommentItem.CommentId)
			if err != nil {
				return nil, err
			}
		}
		return &project_code_api.RemoveCommentResponse{
			Code: project_code_api.RemoveCommentResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addRemoveommentEvent(sessionItem, req, projectItem, commentItem, time.Now().UnixNano()/1e6)
	return ret.(*project_code_api.RemoveCommentResponse), nil
}
